﻿#include "videoDecode.h"
#include "videoPlayer.h"

videoDecode::videoDecode(QObject *parent) :
    QThread(parent)
{

}


videoDecode::~videoDecode()
{

}

void videoDecode::decodeParamInit()
{
    videoPlayer* parent = (videoPlayer *)this->parent();
    if(parent == Q_NULLPTR)
    {
        qDebug()<<"audioDecode::decodeParamInit():   decodeParamInit() 失败 !";
    }
    this->video_file = parent->getVideoName();
    this->videoDecodeParam.Decoding = parent->getDecodingRelated();//获取初始初始化好的解码器信息
    this->videoDecodeParam.UIPlay = parent->getPlayRelated();

    /*申请转码需要使用的空间*/
    this->videoDecodeParam.SRC_VIDEO_pFrame = av_frame_alloc();
    this->videoDecodeParam.RGB24_pFrame = av_frame_alloc();// 存放解码后YUV数据的缓冲区
    this->videoDecodeParam.out_buffer_rgb=nullptr; //解码后的rgb数据

    //将解码后的数据转成RGB24格式
    this->videoDecodeParam. img_convert_ctx = sws_getContext(this->videoDecodeParam.Decoding->format_ctx->streams[this->videoDecodeParam.Decoding->videoStreamIndex]->codecpar->width,
                                     this->videoDecodeParam.Decoding->format_ctx->streams[this->videoDecodeParam.Decoding->videoStreamIndex]->codecpar->height,
            this->videoDecodeParam.Decoding->format_ctx->streams[this->videoDecodeParam.Decoding->videoStreamIndex]->codec->pix_fmt,
            this->videoDecodeParam.Decoding->format_ctx->streams[this->videoDecodeParam.Decoding->videoStreamIndex]->codecpar->width,
            this->videoDecodeParam.Decoding->format_ctx->streams[this->videoDecodeParam.Decoding->videoStreamIndex]->codecpar->height,
            AV_PIX_FMT_RGB24,
            SWS_BICUBIC,
            nullptr,
            nullptr,
            nullptr);

    this->videoDecodeParam. numBytes = avpicture_get_size(AV_PIX_FMT_RGB24,
               this->videoDecodeParam.Decoding->format_ctx->streams[this->videoDecodeParam.Decoding->videoStreamIndex]->codecpar->width,
               this->videoDecodeParam.Decoding->format_ctx->streams[this->videoDecodeParam.Decoding->videoStreamIndex]->codecpar->height);

    this->videoDecodeParam. out_buffer_rgb = (uint8_t *) av_malloc(this->videoDecodeParam. numBytes * sizeof(uint8_t));
    avpicture_fill((AVPicture *)this->videoDecodeParam. RGB24_pFrame,
                   this->videoDecodeParam. out_buffer_rgb,
                   AV_PIX_FMT_RGB24,
            this->videoDecodeParam.Decoding->format_ctx->streams[this->videoDecodeParam.Decoding->videoStreamIndex]->codecpar->width,
            this->videoDecodeParam.Decoding->format_ctx->streams[this->videoDecodeParam.Decoding->videoStreamIndex]->codecpar->height);

}

void videoDecode::decodeParamDelete()
{
    /*释放转码需要使用的空间*/
    av_frame_free(&(this->videoDecodeParam.SRC_VIDEO_pFrame));
    av_frame_free(&(this->videoDecodeParam.RGB24_pFrame));
    av_free(this->videoDecodeParam.out_buffer_rgb);
    sws_freeContext(this->videoDecodeParam.img_convert_ctx);
}

int videoDecode::threadStartUp()
{
    this->decodeParamInit();
    this->start();
    return 0;
}

int videoDecode::threadStop()
{
    this->decodeParamDelete();
    return 0;
}

void videoDecode::run()
{
    qDebug() << tr("videoDecode::run():   视频xian'chen给启动成功, 开始视频解码!");
    bool *videoFileEnd = &(this->videoDecodeParam.UIPlay->threadControl.videoFileEnd);
    int videoStreamIndex = this->videoDecodeParam.Decoding->videoStreamIndex;
    AVFormatContext* format_ctx = this->videoDecodeParam.Decoding->format_ctx;
    AVFrame *RGB24_pFrame = this->videoDecodeParam.RGB24_pFrame;
    AVFrame *SRC_VIDEO_pFrame= this->videoDecodeParam.SRC_VIDEO_pFrame;
    uint8_t *out_buffer_rgb = this->videoDecodeParam.out_buffer_rgb;
    int numBytes = this->videoDecodeParam.numBytes;
    struct SwsContext *img_convert_ctx = this->videoDecodeParam.img_convert_ctx;  //用于解码后的视频格式转换
    /*开始进行视频帧解码*/
    AVPacket pkt;
    AVPacket *packet = &pkt;
    int re;
    double* video_clock = &(this->videoDecodeParam.Decoding->video_clock);
    double* audio_clock = &(this->videoDecodeParam.Decoding->audio_clock);
    while(1)
    {
        switch (this->videoDecodeParam.UIPlay->threadControl.runningStat)
        {
        case PLAY_STOP:
        {
            this->videoDecodeParam.UIPlay->threadSync.videoThreadStat = PLAY_STOP;
            goto END;
        }
            break;
        case PLAY_RUNNING:
        {
            this->videoDecodeParam.UIPlay->threadSync.videoThreadStat = PLAY_RUNNING;
            //判断队列里是否有数据,没有数据就等待一下
            if(this->videoDecodeParam.Decoding->videoAudioAVPacketQueue. get_video_pack_cnt() <= 0)
            {
                //判断文件是否读取完毕
                if(*videoFileEnd)
                {
                    qDebug()<<tr("videoDecode::run():   文件读取完毕, 视频队列数据处理完毕, 结束视频解码线程!");
                    break;
                }
                qDebug() << tr("videoDecode::run():   视频队列中没有数据了, 等待一下!");
                msleep(10);
                continue;
            }

            //取出一帧数据
            //qDebug()<<"videoDecode::run():  从video队列中取出一帧数据";
            pkt = this->videoDecodeParam.Decoding->videoAudioAVPacketQueue .read_video_pack();

            //解码视频 frame
             re = avcodec_send_packet(format_ctx->streams[videoStreamIndex]->codec,&pkt);//发送视频帧
             if (re != 0)
             {
                 av_packet_unref(&pkt);//不成功就释放这个pkt
                 continue;
             }
             re = avcodec_receive_frame(format_ctx->streams[videoStreamIndex]->codec, SRC_VIDEO_pFrame);//接受后对视频帧进行解码
             if (re != 0)
             {
                 av_packet_unref(&pkt);//不成功就释放这个pkt
                 continue;
             }
              //转格式
             sws_scale(img_convert_ctx,
                     (uint8_t const **) SRC_VIDEO_pFrame->data,
                     SRC_VIDEO_pFrame->linesize, 0,
                     format_ctx->streams[videoStreamIndex]->codecpar->height,
                     RGB24_pFrame->data,
                     RGB24_pFrame->linesize);

             //计算当前帧再视频中的时间位置
             //qDebug()<<"视频帧显示的时间戳:"<<SRC_VIDEO_pFrame->pts*av_q2d(video_play_state.format_ctx->streams[video_play_state.video_stream_index]->time_base);

             //当前视频帧的时间位置
             *video_clock = SRC_VIDEO_pFrame->pts*av_q2d(format_ctx->streams[videoStreamIndex]->time_base);
             while(1)
             {
                 //qDebug() << "videoDecode::run():  音频同步中......";
                 //判断是否退出
                 if(PLAY_STOP == this->videoDecodeParam.UIPlay->threadControl.runningStat)
                 {
                     break;
                 }
                 //判断文件是否读取完毕,音频数据是否播放完毕
                 if(   this->videoDecodeParam.UIPlay->threadControl. videoFileEnd == true
                    && this->videoDecodeParam.Decoding->videoAudioAVPacketQueue .get_audio_pack_cnt() <= 0)
                 {
                     break;
                 }
                  //判断是否可以继续播放视频---音频同步
                 if (*audio_clock >= *video_clock)
                 {
                     sendPlayTime(*video_clock);
                     break;
                 }
                 //计算差值时间，转为毫秒单位
                 int delayTime = (*video_clock - *audio_clock) * 1000;
                 if( PLAY_RUNNING != this->videoDecodeParam.UIPlay->threadControl.runningStat)
                 {
                    break;
                 }
                 else if(delayTime > 50)
                 {
                     msleep(50);
                 }
             }
             //加载图片数据
             QImage image(out_buffer_rgb,
                     format_ctx->streams[videoStreamIndex]->codecpar->width,
                     format_ctx->streams[videoStreamIndex]->codecpar->height,
                     QImage::Format_RGB888);
             image=image.scaled(this->videoDecodeParam.UIPlay->imageDisplaySize, Qt::KeepAspectRatio, Qt::SmoothTransformation);

             // VideoDataOutput(image.scaled(640,480,Qt::KeepAspectRatio, Qt::SmoothTransformation)); //发送信号
             sendImg(image); //发送信号

             //取消引用数据包引用的缓冲区并重置
             av_packet_unref(&pkt);
        }
            break;
        case PLAY_PAUSE:
        {
            this->videoDecodeParam.UIPlay->threadSync.videoThreadStat = PLAY_PAUSE;
            msleep(100); //暂停播放
        }
            break;
        case PLAY_SEEK:
        {
            this->videoDecodeParam.UIPlay->threadSync.videoThreadStat = PLAY_SEEK;
            while(PLAY_SEEK != this->videoDecodeParam.UIPlay->threadSync.playerThreadStat) //等待player先执行 PLAY_SEEK 相关代码
            {
                msleep(10);
            }
            int ret ;
            int64_t seek_target = this->videoDecodeParam.UIPlay->videoSeekTime * AV_TIME_BASE;
            int64_t cur_target;
            AVPacket pkt;
            do
            {
                if(PLAY_SEEK != this->videoDecodeParam.UIPlay->threadControl.runningStat)
                {
                    break;
                }
                ret = av_read_frame(format_ctx, &pkt);
                if(ret < 0)
                {
                    qDebug() << tr("videoDecode::run(): case PLAY_SEEK:  到了文件尾");
                    this->videoDecodeParam.UIPlay->threadControl.runningStat = PLAY_FINISHED;
                }
                if(pkt.stream_index == videoStreamIndex)
                {
                    cur_target = pkt.pts*av_q2d(format_ctx->streams[videoStreamIndex]->time_base)*AV_TIME_BASE;
                    qDebug() << tr("seek_target : %1, cur_target: %2").arg(seek_target).arg(cur_target);
                    //解码视频 frame
                     re = avcodec_send_packet(format_ctx->streams[videoStreamIndex]->codec,&pkt);//发送视频帧
                     if (re != 0)
                     {
                         av_packet_unref(&pkt);//不成功就释放这个pkt
                         continue;
                     }
                     re = avcodec_receive_frame(format_ctx->streams[videoStreamIndex]->codec, SRC_VIDEO_pFrame);//接受后对视频帧进行解码
                     if (re != 0)
                     {
                         av_packet_unref(&pkt);//不成功就释放这个pkt
                         continue;
                     }
                     cur_target = SRC_VIDEO_pFrame->pts*av_q2d(format_ctx->streams[videoStreamIndex]->time_base)*AV_TIME_BASE;
                     seek_target = this->videoDecodeParam.UIPlay->videoSeekTime * AV_TIME_BASE;
                }
                av_packet_unref(&pkt);
            }while(cur_target < seek_target);
            this->videoDecodeParam.UIPlay->threadControl.runningStat = PLAY_RUNNING;
        }
            break;
        case PLAY_FINISHED:
        {
            this->videoDecodeParam.UIPlay->threadSync.videoThreadStat = PLAY_FINISHED;
            msleep(100); //播放完成, 但是还没有释放解码相关资源, 此时还可以往回跳转
        }
            break;
        } //switch (this->audioDecodeParam.UIPlay->threadControl.runningStat)

    } //while(1){}

END:
    this->threadStop();
    qDebug() << tr("视频解码线程退出成功.\n");
}

